package userService

import (
	"encoding/json"
	"gin/common/lib/redis"
	"gin/config"
	"gin/dao/entity"
	"gin/dao/mapper/users"
	"gin/dao/mapper/users/usersInfo"
	"gin/model/body"
	"gin/model/http_error"
	"gin/util/compute"
	"gin/util/cryp"
	error2 "gin/util/error"
	"gin/util/jwt"
	"gin/util/tool"
	"gin/util/validator"
	"time"

	redis2 "github.com/go-redis/redis/v8"

	"github.com/sirupsen/logrus"
)

// UserTokenRedis 用户token redis存储结构体
type UserTokenRedis struct {
	Uid        int64  `json:"uid"`        // 用户id
	Token      string `json:"token"`      // 用户token
	UserType   byte   `json:"userType"`   // 0 普通用户，1 admin
	ExpireTime int64  `json:"expireTime"` // token到期时间
}

// CheckToken 校验token
func CheckToken(uid int64, token, channel string) (bool, string) {
	if uid == 0 || token == "" {
		return false, ""
	}

	key := redis.GetRedisKey("TOKEN", uid)

	if channel != "" {
		key = redis.GetRedisKey("TOKEN", uid, channel)
	}

	var userTokenRedis UserTokenRedis
	err := redis.GetObject(key, &userTokenRedis)
	if err != nil {
		if err != redis2.Nil {
			logrus.Error("Redis 异常：", err.Error())
			return true, ""
		}
	}

	if userTokenRedis.Uid > 0 {
		// 账号已经在其它设备登录
		if userTokenRedis.Token != token {
			return false, http_error.JwtError.ErrorMsg
		}
		// 你的登录状态过期，请重新登录
		if userTokenRedis.ExpireTime < time.Now().Unix() {
			return false, http_error.JwtError.ErrorMsg
		}
	} else {
		// 检查是否用户是否被禁用
		//CheckUserEnable(uid)
		return false, http_error.JwtError.ErrorMsg
	}
	return true, ""
}

// CheckUserLogin 校验登录
func CheckUserLogin(p body.UserLoginParam) entity.Users {
	userInfo, err := users.GetUserByLogin(p.UserLogin)

	if userInfo.Id == 0 || userInfo.UserStatus == 9 || err != nil {
		panic(http_error.AccountNotExist)
	}
	pass := jwt.SetPass(cryp.AesDecryptCBC(p.Password, config.KEY.LoginAesKey), userInfo.PrivateKey)

	// 密码不匹配
	if userInfo.UserPass != pass {
		panic(http_error.AccountOrPasswordError)
	}

	if userInfo.UserStatus == 0 {
		panic(http_error.AccountBanned)
	}
	return userInfo
}

func GetToken(id int64, p body.UserLoginParam) string {
	token := jwt.SetToken(id, p.UserLogin, p.Channel)

	// if p.Location != "" {
	// 	users.UpdateLocation(id, p.Location)
	// }

	expireTime := token.Expire.Unix() + 60*60*24*300
	now := time.Now()
	tl := now.Format("2006-01-02 15:04:05")

	usersInfo.Update(id, map[string]interface{}{
		"token":           token.Token,
		"expire_time":     expireTime,
		"last_login_time": tl,
		"last_login_ip":   p.Ip,
	})

	tokenRedisInfo := UserTokenRedis{
		Uid:        token.Uid,
		Token:      token.Token,
		ExpireTime: expireTime,
	}

	tokenKey := redis.GetRedisKey("TOKEN", token.Uid)
	//if p.Channel != "" {
	//	tokenKey = redis.GetRedisKey("TOKEN", token.Uid, p.Channel)
	//}

	redis.Set(tokenKey, tokenRedisInfo, 60*60*24*300)

	return token.EncodeToken
}

// VerifyUser 登录校验用户
func VerifyUser(p body.UserLoginParam) (string, string) {
	p.Password = cryp.AesDecryptCBC(p.Password, config.KEY.LoginAesKey)

	// 检查密码格式
	validator.PassCheck(p.Password)

	var email string
	var mobile string

	if validator.IsMail(p.UserLogin) {
		email = p.UserLogin
	} else if validator.IsPhone(p.UserLogin) {
		mobile = p.UserLogin
	} else {
		panic(http_error.IsNotPhoneOrEmail)
	}

	// 用户注销
	if users.CheckUserLogout(p.UserLogin) {
		panic(http_error.AccountLogout)
	}

	return email, mobile
}

// VerifyPassword 校验密码
func VerifyPassword(password string) string {
	decode := cryp.AesDecryptCBC(password, config.KEY.LoginAesKey)
	validator.PassCheck(decode)
	return decode
}

// AddUser 添加用户
func AddUser(userLogin, password, source, email, mobile, ip, hardwareId, avatar string) int64 {

	privateKey := compute.GetRandomString(6)
	password = jwt.SetPass(password, privateKey)

	nicename := tool.RandName()

	if users.CheckUserExist(userLogin) {
		panic(http_error.AccountExist)
	}

	var registerType int8
	if mobile != "" {
		registerType = 1
	}
	if email != "" {
		registerType = 2
	}
	data := entity.Users{}

	data.UserLogin = userLogin
	data.Mobile = mobile
	data.UserEmail = email
	data.UserNickname = nicename
	data.UserPass = password
	data.Signature = ""
	data.Avatar = avatar
	data.AvatarThumb = avatar
	data.UserType = 2
	data.Source = source
	data.PrivateKey = privateKey
	data.HardwareId = hardwareId
	data.RegisterType = registerType
	// data["cover_img"] = portrait.GetRandomPortrait(3)
	// data["pc_cover_img"] = portrait.GetRandomPortrait(2)
	data.UserStatus = 1
	uid := users.Add(data)

	dataInfo := map[string]interface{}{}
	dataInfo["uid"] = uid
	dataInfo["last_login_ip"] = ip
	usersInfo.Add(dataInfo)

	return uid
}

// GetUserInfo 获取用户信息
func GetUserInfo(uid, toUid int64) entity.Users {
	qid := uid
	if toUid > 0 {
		qid = toUid
	}

	userInfo, _ := users.Get(body.UserSearchParam{Uid: qid})

	if userInfo.Id == 0 {
		panic(http_error.AccountNotExist)
	}

	return userInfo
}

// UpdateUserInfo 修改用户信息
func UpdateUserInfo(uid int64, p body.UpdateFieldsParam) {
	fields := map[string]interface{}{}
	json.Unmarshal([]byte(p.Fields), &fields)

	if name, ok := fields["userNickName"]; ok {
		if name.(string) == "" {
			panic(http_error.NicknameIsNil)
		}
	}
	users.UpdateField(uid, fields)
}

// UpateAvatar 修改头像
func UpateAvatar(uid int64, avatar string) {
	if avatar == "" {
		panic(http_error.AvatarIsNil)
	}
	fields := map[string]interface{}{}
	fields["avatar"] = avatar
	fields["avatar_thumb"] = avatar
	users.UpdateField(uid, fields)
}

// DelUserToken 退出所有设备端
func DelUserToken(uid int64) {
	tokenKey := redis.GetRedisKey("TOKEN", uid)
	redis.Del(tokenKey, "")
	usersInfo.DelToken(uid)
}

// UserLogoff 用户注销
func UserLogoff(p body.LogoutParam, uid int64) {
	userInfo, _ := users.Get(body.UserSearchParam{Uid: uid})
	// 用户不存在
	if userInfo.Id == 0 {
		panic(http_error.AccountNotExist)
	}
	//todo:校验验证码

	// 修改用户状态
	users.UpdateField(uid, map[string]interface{}{"user_status": 9})

	//todo:删除关联内容

	redis.Del(redis.GetRedisKey("TOKEN", uid), "")
}

// UserBindEmail 用户绑定邮箱
func UserBindEmail(p body.EmailParam, uid int64) {
	checkUser(uid, p.Password)

	//该邮箱已注册，请更换邮箱重试。
	if users.CheckRegistered(uid, p.Email) {
		panic(http_error.MailRegistered)
	}

	//该邮箱已绑定在另一个手机号上，请更换邮箱重试。
	if users.CheckEmailBind(uid, p.Email) {
		panic(http_error.MailBound)
	}

	// todo:验证码校验

	users.UpdateField(uid, map[string]interface{}{"user_email": p.Email})

}

// UserBindMobile 用户绑定手机
func UserBindMobile(p body.MobileParam, uid int64) {
	checkUser(uid, p.Password)

	// 校验手机是否被注册
	if users.CheckRegistered(uid, p.Phone) {
		panic(http_error.MobileRegistered)
	}

	// todo 验证码校验

	users.UpdateField(uid, map[string]interface{}{"mobile": p.Phone})
}

// UpdatePass 修改密码
func UpdatePass(p body.UpdatePassParam, uid int64) {
	userInfo, _ := users.Get(body.UserSearchParam{Uid: uid})
	// 用户不存在
	if userInfo.Id == 0 {
		panic(http_error.AccountNotExist)
	}

	if p.OldPassword != "" {
		p.OldPassword = cryp.AesDecryptCBC(p.OldPassword, config.KEY.LoginAesKey)
		// 旧密码不匹配
		if userInfo.UserPass != jwt.SetPass(p.OldPassword, userInfo.PrivateKey) {
			panic(http_error.OldPasswordWrong)
		}
	}

	p.NewPassword = cryp.AesDecryptCBC(p.NewPassword, config.KEY.LoginAesKey)

	// 检查新密码格式
	validator.PassCheck(p.NewPassword)
	// 更新密码
	privateKey := compute.GetRandomString(6)
	password := jwt.SetPass(p.NewPassword, privateKey)

	users.UpdateField(uid, map[string]interface{}{"user_pass": password, "private_key": privateKey})

	// 退出登录
	DelUserToken(uid)
}

// checkUser 用户信息校验
func checkUser(uid int64, password string) {
	userInfo, _ := users.Get(body.UserSearchParam{Uid: uid})
	// 用户不存在
	if userInfo.Id == 0 {
		panic(http_error.AccountNotExist)
	}
	pass := jwt.SetPass(cryp.AesDecryptCBC(password, config.KEY.LoginAesKey), userInfo.PrivateKey)
	// 密码不匹配
	if userInfo.UserPass != pass {
		panic(http_error.AccountOrPasswordError)
	}
	// 用户封禁
	if userInfo.UserStatus == 0 {
		panic(http_error.AccountBanned)
	}
}

// AddAttention 添加/取消关注
func AddAttention(uid, toUid int64) {
	id := users.GetExist(uid, toUid)

	if uid == toUid {
		error2.CustomPanic("不能关注自己！")
	}

	users.IsUsersNum(uid)
	users.IsUsersNum(toUid)

	if id == 0 {
		users.AddAttention(uid, toUid)
		users.AddFollowNum(toUid)
		users.AddAttentionNum(uid)
	} else {
		users.DelAttention(id)
		users.DelFollowNum(toUid)
		users.DelAttentionNum(uid)
	}
}

// GetNum 获取关注和粉丝数量
func GetNum(uid int64) entity.UsersNum {
	return users.GetNum(uid)
}

// GetAttentionList 获取关注列表
func GetAttentionList(p body.ToUidListParam) ([]*entity.UsersAttentionList, int) {
	list := users.GetAttentionList(p)
	info := users.GetNum(p.ToUid)
	return list, info.AttentionNum
}

// GetFansList 获取粉丝列表
func GetFansList(p body.ToUidListParam) ([]*entity.UsersAttentionList, int) {
	list := users.GetFansList(p)
	followIds := make([]int64, 0)
	for _, v := range list {
		followIds = append(followIds, v.FollowId)
	}
	uidMap := users.IsCorrelation(p.ToUid, followIds)
	for i, v := range list {
		if uidMap[v.FollowId] == 1 {
			list[i].IsCorrelation = true
		}
	}

	info := users.GetNum(p.ToUid)
	return list, info.FollowNum
}

// IsAttention 判断是否关注
func IsAttention(uid, toUid int64) bool {
	id := users.GetExist(uid, toUid)
	return id > 0
}

// GetAttentionIds 获取关注id
func GetAttentionIds(uid int64) []int64 {
	return users.GetAttentionIds(uid)
}
